﻿/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2017 @ ShenZhen ,China
*******************************************************************************/
#include "File.h"
#include "Tracer.h"
#include "CommandPipe.h"

#include <stdio.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
/**
 *  \brief  File构造函数
 *  \param  none
 *  \note   none
 */
File::File():
m_fp(NULL),
m_name(""),
m_mmaddr(NULL)
{
}

File::File(const std::string fileName,int ioMode):
m_fp(NULL),
m_name(""),
m_mmaddr(NULL)
{
    this->open(fileName.c_str(),ioMode);
}
File::~File()
{
    this->close();
}

/**
 *  \brief  判断文件是否存在
 *  \param  filePath 文件名
 *  \return 文件存在返回true,否则返回false
 *  \note   只有name是文件名称时才返回true,如果是目录名则返回false
 */
bool File::isExist(const char* filePath)
{
    if (0==access(filePath,F_OK))
    {
        struct stat fileStat;
        if (0!=lstat(filePath,&fileStat)){
            return false;
        }
        if (S_ISREG(fileStat.st_mode)||
            S_ISLNK(fileStat.st_mode)) {
            return true;
        }
    }
    return false;
}
/**
 *  \brief  删除文件
 *  \param  fileName 文件名
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool File::deleteFile(const char* fileName)
{
    if (remove(fileName)!=0) {
        TRACE_ERR("File::delFile(%s) error:%s.\n", fileName, ERROR_STRING);
        return false;
    }
    return true;
}
/**
 *  \brief  获取内容大小
 *  \param  filePath 文件名或文件夹名
 *  \return 返回文件的大小,以byte为单位
 *  \note   none
 */
int File::getContentSize(const char* fileName)
{
    int size=0;
    std::string file = fileName;
    #ifdef OS_ANDROID
    std::string cmd = "du -d 0 ";
    #else
    std::string cmd = "du --max-depth=0 -b ";
    #endif
    cmd += fileName;
    std::string result = CommandPipe::execute(cmd);
    if (result.empty()) {
        return STATUS_ERROR;
    }
    size = atoi(result.c_str());
    return size;
}

/**
 *  \brief  打开文件
 *  \param  fileName 文件全名(包含路径,如:/etc/init.d/rcS)
 *  \param  ioMode 打开模式IO_MODE_E
 *  \return 文件打开成功返回true,失败返回false
 *  \note   none
 */
bool File::open(const char* fileName, int ioMode)
{
    std::string modestr;
    if (fileName==NULL)
    {
        TRACE_ERR_CLASS("file name is NULL.\n");
        return false;
    }

    switch (ioMode)
    {
        case IO_MODE_RD_ONLY:
            modestr = "rb";
            break;
        case IO_MODE_WR_ONLY:
        case IO_MODE_RDWR_ONLY:
			modestr = "rb+";
			break;
        case IO_MODE_REWR_ORNEW:
            modestr = "wb";
            break;
        case IO_MODE_RDWR_ORNEW:
            modestr = "wb+";
            break;
		case IO_MODE_APPEND_ORNEW:
			modestr = "ab";
			break;
        case IO_MODE_RDAPPEND_ORNEW:
			modestr = "ab+";
			break;
        default:
            TRACE_ERR_CLASS("Unsupport mode(%d)\n");
            return false;
    }
	
	if (m_fp != NULL)
    {
        fclose(m_fp);
        m_fp = NULL;
    }

    m_fp = fopen(fileName, modestr.c_str());
    if (NULL == m_fp)
    {
        TRACE_ERR_CLASS("open file:%s error(%s).\n",fileName,ERROR_STRING);
        return false;
    }

    m_name = fileName;
    //TRACE_DBG_CLASS("open file:%s\n",m_name.c_str());
    return true;
}

/**
 *  \brief  关闭文件
 *  \param  void
 *  \return 文件关闭成功返回true,失败返回false
 *  \note   none
 */
bool File::close()
{
    if (m_fp != NULL)
    {
        fclose(m_fp);
        m_fp = NULL;
    }
    //TRACE_DBG_CLASS("close file:%s\n",m_name.c_str());
    return true;
}

/**
 *  \brief  读取数据
 *  \param  buf 数据缓存
 *  \param  count 要读取的长度
 *  \return 成功返回读取的数据长度,失败返回STATUS_ERROR
 *  \note   none
 */
int File::readData(char * buf, int count, int timeoutMs)
{
	if(NULL == m_fp)
	{
		TRACE_ERR_CLASS("File not open.\n");
		return STATUS_ERROR;
	}
    if (NULL == buf || count <= 0)
    {
    	TRACE_ERR_CLASS("parameter error:buf=0x%x,count=%d.\n",buf,count);
        return STATUS_ERROR;
    }
    return fread(buf, 1, count, m_fp);
}

/**
 *  \brief  写入数据
 *  \param  buf 数据缓存
 *  \param  count 要写入的长度
 *  \return 成功返回写入的数据长度,失败返回STATUS_ERROR
 *  \note   none
 */
int File::writeData(const char * buf, int count, int timeoutMs)
{
    int ret;
    if(NULL == m_fp)
	{
		TRACE_ERR_CLASS("File not open.\n");
		return STATUS_ERROR;
	}
    if (NULL == buf || count <= 0)
    {
    	TRACE_ERR_CLASS("parameter error.\n");
        return STATUS_ERROR;
    }
    ret = fwrite(buf, 1, count, m_fp);
    fflush(m_fp);
    return ret;
}

/**
 *  \brief  将文件映射到内存
 *  \return 映射成功返回地址,失败返回NULL
 *  \note   在使用mapMemery时,请不要使用readData,writeData这些接口
 */
void* File::mapMemory()
{
    if (m_mmaddr==NULL)
    {
        m_mmaddr = mmap(NULL,getSize(),PROT_READ|PROT_WRITE,MAP_SHARED,fileno(m_fp),0);
        if (MAP_FAILED==m_mmaddr)
        {
            TRACE_ERR_CLASS("file[%s] map memory error:%s!\n",m_name.c_str(),ERROR_STRING);
            return NULL;    
        }
    }
    return m_mmaddr;
}

/**
 *  \brief  解除文件内存映射
 *  \return 成功返回STATUS_OK,失败返回STATUS_OK
 *  \note   在使用mapMemery时,请不要使用readData,writeData这些接口
 */
int File::unmapMemory()
{
    if (m_mmaddr!=NULL)
    {
        if (-1==munmap(m_mmaddr,getSize()))
        {
            TRACE_ERR_CLASS("file[%s] unmap memory error:%s!\n",m_name.c_str(),ERROR_STRING);
            return STATUS_ERROR;   
        }
        m_mmaddr = NULL;
    }
    return STATUS_OK;
}


/**
 *  \brief  读取一行数据
 *  \param  lineStr 该行字符串
 *  \return 成功返回读取的数据长度,失败返回STATUS_ERROR
 *  \note   none
 */
int File::readLine(std::string& lineStr)
{
	int ret;
	if(NULL == m_fp)
	{
		TRACE_ERR_CLASS("File not open.\n");
		return STATUS_ERROR;
	}
	lineStr.clear();
	while(1)
	{
		char buf[2]={0};
		ret=readData(buf,1);
		if (STATUS_ERROR==ret)
		{
			return STATUS_ERROR;
		}
		else if(0==ret)
		{
			if (isEnd())
			{
				return lineStr.size();
			}
			else
			{
				return STATUS_ERROR;
			}
		}
		else
		{
			lineStr += buf;
			if(std::string::npos!=lineStr.find("\r\n"))
			{
				lineStr = lineStr.substr(0,lineStr.size()-2);
				return lineStr.size();
			}
			else if (std::string::npos!=lineStr.find("\n"))
			{
				lineStr = lineStr.substr(0,lineStr.size()-1);
				return lineStr.size();
			}			
		}
	}
	
}

/**
 *  \brief  获取文件大小
 *  \param  void
 *  \return 成功返回文件大小,失败返回STATUS_ERROR
 *  \note   none
 */
int File::getSize()
{
    int fileSize, pos;
    if(NULL == m_fp)
	{
		TRACE_ERR_CLASS("File not open.\n");
		return STATUS_ERROR;
	}
    pos = ftell(m_fp);			/* 保存当前读写位置 */
    fseek(m_fp, 0, SEEK_END);
    fileSize = ftell(m_fp);
	if (fileSize<0)
	{
		fileSize = STATUS_ERROR;
	}
    fseek(m_fp, pos, SEEK_SET);	/* 恢复当前读写位置 */
    return fileSize;
}
/**
 *  \brief  获取文件名称
 *  \param  void
 *  \return 返回当前文件名称
 *  \note   none
 */
std::string File::getName()
{
    return m_name;
}
/**
 *  \brief  获取文件句柄
 *  \param  void
 *  \return 返回当前文件句柄(FILE*)
 *  \note   none
 */
FILE* File::getFp()
{
    return m_fp;
}
/**
 *  \brief  获取文件描述符
 *  \param  void
 *  \return 返回当前文件描述符
 *  \note   none
 */
int File::getFd()
{
    return fileno(m_fp);
}
/**
 *  \brief  判断是否已经到文件尾部
 *  \param  void
 *  \return 是返回true,否返回false
 *  \note   none
 */
bool File::isEnd()
{
	int pos = getPos();
    if (pos==STATUS_ERROR || pos!=getSize())
    {
        return false;
    }
    return true;
}

/**
 *  \brief  获取当前读写位置
 *  \param  void
 *  \return 成功返回当前读写位置,失败返回STATUS_ERROR
 *  \note   none
 */
int File::getPos()
{
    int pos;
    if(NULL == m_fp)
	{
		TRACE_ERR_CLASS("File not open.\n");
		return STATUS_ERROR;
	}
    pos = ftell(m_fp);
    if (pos < 0)
    {
        return STATUS_ERROR;
    }
    else
    {
        return pos;
    }
}

/**
 *  \brief  设置当前读写位置
 *  \param  void
 *  \return 成功返回OK,失败返回STATUS_ERROR
 *  \note   none
 */
int File::setPos(int pos)
{
    if(NULL == m_fp)
	{
		TRACE_ERR_CLASS("File not open.\n");
		return STATUS_ERROR;
	}
    if (0 == fseek(m_fp, pos, SEEK_SET))
    {
        return STATUS_OK;
    }
    else
    {
        return STATUS_ERROR;
    }

}